package org.codeblue.support.web.interceptor;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.codeblue.support.beans.MethodStats;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.NamedThreadLocal;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

/**
 * @ClassName: SystemInterceptor
 * @Description: TODO(服务器性能分析统计拦截器)
 */
public class SystemInterceptor extends HandlerInterceptorAdapter {
	private Log log = LogFactory.getLog(getClass());

	private static ConcurrentHashMap<String, MethodStats> methodStats = new ConcurrentHashMap<String, MethodStats>();
	private  long statLogFrequency = 10;
	private  long methodWarningThreshold = 1000;

	private NamedThreadLocal<Long> startTimeThreadLocal = new NamedThreadLocal<Long>(
			"StopWatch-StartTime");

	/*
	 * (非 Javadoc) <p>Title: afterCompletion</p> <p>Description:
	 * 该方法也是需要当前对应的Interceptor 的preHandle 方法的返回值为true 时才会执行</p>
	 * 
	 * @param request
	 * 
	 * @param response
	 * 
	 * @param obj
	 * 
	 * @param e 可以根据ex是否为null判断是否发生了异常，进行日志记录
	 * 
	 * @throws Exception
	 * 
	 * @see
	 * org.springframework.web.servlet.HandlerInterceptor#afterCompletion(javax
	 * .servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse,
	 * java.lang.Object, java.lang.Exception)
	 */
	@Override
	public void afterCompletion(HttpServletRequest request,
			HttpServletResponse response, Object obj, Exception e)
			throws Exception {
		long endTime = System.currentTimeMillis();// 2、结束时间
		long beginTime = startTimeThreadLocal.get();// 得到线程绑定的局部变量（开始时间）
		long consumeTime = endTime - beginTime;// 3、消耗的时间
		if (consumeTime > 500) {// 此处认为处理时间超过500毫秒的请求为慢请求

			final String basePath = request.getScheme() + "://"
					+ request.getServerName() + ":" + request.getServerPort();// +path;
			log.info(String.format("%s%s 响应时间： %d ms", basePath,
					request.getRequestURI(), consumeTime));

			HandlerMethod method = (HandlerMethod) obj;
			updateStats(method.getMethod().getDeclaringClass().getName()
					+ "." + method.getMethod().getName(), consumeTime);

			super.afterCompletion(request, response, obj, e);
		}
	}

	/*
	 * (非 Javadoc) <p>Title: postHandle</p> <p>Description: afterCompletion
	 * 方法都只能是在当前所属的Interceptor 的preHandle 方法的返回值为true 时才能被调用</p>
	 * 
	 * @param request
	 * 
	 * @param response
	 * 
	 * @param obj
	 * 
	 * @param model
	 * 
	 * @throws Exception
	 * 
	 * @see
	 * org.springframework.web.servlet.HandlerInterceptor#postHandle(javax.servlet
	 * .http.HttpServletRequest, javax.servlet.http.HttpServletResponse,
	 * java.lang.Object, org.springframework.web.servlet.ModelAndView)
	 */
	public void postHandle(HttpServletRequest request,
			HttpServletResponse response, Object obj, ModelAndView model)
			throws Exception {
		//final ObjectMapper om = new ObjectMapper();
		if(model!=null){
			log.info("ModelAndView信息如下：\n\tmodel:"+(model.getModel())
					+"\n\tmodelMap:"+(model.getModelMap())
					+"\n\tViewName:"+model.getViewName());
		}
		
		super.postHandle(request, response, obj, model);

	}

	/*
	 * (非 Javadoc) <p>Title: preHandle</p> <p>Description:
	 * 该方法将在请求处理之前进行调用.回值为false，当preHandle的返回值为false的时候整个请求就结束了.可以进行编码、安全控制等处理</p>
	 * 
	 * @param request
	 * 
	 * @param response
	 * 
	 * @param obj
	 * 
	 * @return
	 * 
	 * @throws Exception
	 * 
	 * @see
	 * org.springframework.web.servlet.HandlerInterceptor#preHandle(javax.servlet
	 * .http.HttpServletRequest, javax.servlet.http.HttpServletResponse,
	 * java.lang.Object)
	 */
	@Override
	public boolean preHandle(HttpServletRequest request,
			HttpServletResponse response, Object obj) throws Exception {
		
		if(log.isDebugEnabled()){
			Map map = request.getParameterMap();
			Iterator it =  map.keySet().iterator();
			String logParam = "" ;
			String requestParam = "?";
			while(it.hasNext()){
				String key = (String)it.next();
				String value[]= (String[])map.get(key);
				logParam += (" ["+key+":"+value[0]+"] ");
				requestParam += (key+"="+value[0]+"&") ;
			
			}
			log.info("httpRequstFormatParam:"+ logParam);
			log.info("httpRequestURL: "+ request.getRequestURL() + requestParam);
			SimpleDateFormat formatters = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
			String timestring = formatters.format(new Date());
			log.info("httpRequestTime: "+ timestring);
		}
		long beginTime = System.currentTimeMillis();// 1、开始时间
		startTimeThreadLocal.set(beginTime);// 线程绑定变量（该数据只有当前请求的线程可见）
		/*HandlerMethod method = (HandlerMethod) obj;
		System.out.println();*/
		return true;
	}

	/**
	 * @Title: updateStats
	 * @Description: TODO(更新内存中的时间统计)
	 * @param methodName
	 * @param elapsedTime void  返回类型 
	 */
	private void updateStats(String methodName, long elapsedTime) {
		MethodStats stats = methodStats.get(methodName);
		if (stats == null) {
			stats = new MethodStats(methodName);
			methodStats.put(methodName, stats);
		}
		
		stats.count++;
		stats.totalTime += elapsedTime;
		if (elapsedTime > stats.maxTime) {
			stats.maxTime = elapsedTime;
		}

		if (elapsedTime > methodWarningThreshold) {
			log.warn("method warning: " + methodName + "(), cnt = "
					+ stats.count + ", lastTime = " + elapsedTime
					+ ", maxTime = " + stats.maxTime);
		}

		if (stats.count % statLogFrequency == 0) {
			long avgTime = stats.totalTime / stats.count;
			long runningAvg = (stats.totalTime - stats.lastTotalTime)
					/ statLogFrequency;
			log.debug("method: " + methodName + "(), cnt = " + stats.count
					+ ", lastTime = " + elapsedTime + ", avgTime = " + avgTime
					+ ", runningAvg = " + runningAvg + ", maxTime = "
					+ stats.maxTime);

			// reset the last total time
			stats.lastTotalTime = stats.totalTime;
		}else{
			log.debug("method: " + methodName + "(), cnt = " + stats.count
					+ ", lastTime = " + elapsedTime + ", maxTime = "
					+ stats.maxTime);
		}
	}

}
